package cn.hdu.liang.ch7;

import java.util.function.Function;
import java.util.stream.LongStream;
import java.util.stream.Stream;

public class ParallelStream {
    public static void main(String[] args) {
        /*System.out.println(sequentialSum(100));
        System.out.println(iterativeSum(100));
        System.out.println(parallelSum(100));
        System.out.println(Runtime.getRuntime().availableProcessors());*/

        //System.out.println("求和运算时间" + measureSumPerf(ParallelStream::sequentialSum, 2000_0000) + "毫秒");
        //System.out.println("求和运算时间" + measureSumPerf(ParallelStream::iterativeSum, 2000_0000) + "毫秒");
        //System.out.println("求和运算时间" + measureSumPerf(ParallelStream::parallelSum, 2000_0000) + "毫秒");
        //System.out.println("求和运算时间" + measureSumPerf(ParallelStream::rangeSum, 2000_0000) + "毫秒");
        //System.out.println("求和运算时间" + measureSumPerf(ParallelStream::parallelRangedSum, 2000_0000) + "毫秒");
        System.out.println("求和运算时间" + measureSumPerf(ForkJoinSumCalculator::forkJoinSum, 2000_0000) + "毫秒");

        //System.out.println("求和运算时间" + measureSumPerf(ParallelStream::sideEffectSum, 2000_0000) + "毫秒");
        //System.out.println("求和运算时间" + measureSumPerf(ParallelStream::sideEffectParallelSum, 2000_0000) + "毫秒");

    }

    public static long sideEffectParallelSum(long n) {
        Accumulator acc = new Accumulator();
        LongStream.rangeClosed(1, n).parallel().forEach(acc::add);
        return acc.total;
    }

    public static long sideEffectSum(long n) {
        Accumulator acc = new Accumulator();
        LongStream.rangeClosed(1, n).forEach(acc::add);
        return acc.total;

    }

    public static class Accumulator {
        public long total = 0;

        public void add(long value) {
            total += value;
        }
    }

    public static long rangeSum(long n) {
        return LongStream.rangeClosed(1, n)
                .reduce(0L, Long::sum);
    }

    public static long parallelRangedSum(long n) {
        return LongStream.rangeClosed(1, n)
                .parallel()
                .reduce(0L, Long::sum);
    }

    public static long measureSumPerf(Function<Long, Long> adder, long n) {
        long fastest = Long.MAX_VALUE;
        for(int i = 0; i < 5; i++) {
            long start = System.nanoTime();
            long sum = adder.apply(n);
            long duration = (System.nanoTime() - start) / 100_0000;
            System.out.println("Result: " + sum);
            if(duration < fastest) {
                fastest = duration;
            }
        }
        return fastest;
    }

    public static long parallelSum(long n) {
        return Stream.iterate(1L, i -> i + 1)
                .limit(n)
                .parallel()
                .reduce(0L, Long::sum);
    }

    public static long sequentialSum(long n) {
        return Stream.iterate(1L, i -> i + 1)
                .limit(n)
                .reduce(0L, Long::sum);
    }

    public static long iterativeSum(long n) {
        long result = 0L;
        for(long i = 0; i <= n; i++) {
            result += i;
        }
        return result;
    }
}
